//这些题目都是面试或者考试的真题，务必务必全部搞懂


//1.一维数组
//#include<stdio.h>
//int main(void)
//{
//    int a={1,2,3,4};
//    printf("%d\n",sizeof(a));   //只有单独的数组名才是检测整个数组的大小
//    printf("%d\n",sizeof(a+0));//这个大小还是4/8。   数组名代表首元素的地址，a+0还是代表首元素的地址   int地址的大小就是4/8
//    printf("%d\n",sizeof((*a));
//    printf("%d\n",sizeof(a+1));
//    printf("%d\n",sizeof(a[1]));
//    printf("%d\n",sizeof(&a));//4/8  取出的是本数组的地址，只要是地址的大小都是4个字节      也就是所有的指针类型的大小都是4
//    printf("%d\n",sizeof(*&a));
//    printf("%d\n",sizeof(&a+1);
//    printf("%d\n",sizeof(&a[0]));
//    printf("%d\n",sizeof(&a[0]+1);
//}





//2.字符数组            //strlen(const char* string)
//#include<stdio.h>
//#include<string.h>
//int main(void)
//{
//    char arr[]={'a','b','c','d','e','f'};
//    printf("%d\n",sizeof(arr));//大小是6，sizeof计算的是数组的大小。只用字符串中才带有\0，并且占一个元素
//    printf("%d\n",sizeof(arr+0));//4/8字节   数组元素字节的大小和地址的大小没有关系
//    printf("%d\n",sizeof(*arr));
//    printf("%d\n",sizeof(arr[1]));
//    printf("%d\n",sizeof(&arr));
//    printf("%d\n",sizeof(&arr+1));
//    printf("%d\n",sizeof(&arr[0]+1));
//
//    printf("%d\n",strlen(arr));//随机值    上述的字符数组不是字符串数组，只用字符串数组才拥有\0
//               strlen只能计算字符串字符的个数，里面放的只能是地址。放的如果是数字，他就会访问这个数字的地址

//    printf("%d\n",strlen(arr+0));//随机值
//    printf("%d\n",strlen(*arr));//这种写法就是错误的，首元素进行解引用是a，a的ascll是97，也就是会访问97这个地址
//    printf("%d\n",strlen(arr[1]));//错误的   和上面的这个原因是一样的
//    printf("%d\n",strlen(&arr));//随机值
//    printf("%d\n",strlen(&arr+1));
//    printf("%d\n",strlen(&arr[0]+1));

    //总结：strlen函数只能接收char*类型的地址

//}



//3.字符串数组
//#include<stdio.h>
//#include<string.h>
//int main(void)
//{
//    char arr[]="abcdef";//字符串后面拥有\0,相当于这个数组被初始化为arr[7]
////    printf("%d\n",sizeof(arr));
////    printf("%d\n",sizeof(arr+0));
////    printf("%d\n",sizeof(*arr));
////    printf("%d\n",sizeof(arr[1]));
////    printf("%d\n",sizeof(&arr));
////    printf("%d\n",sizeof(&arr+1));
////    printf("%d\n",sizeof(&arr[0]+1));
//
//
//    printf("%d\n",strlen(arr));
//    printf("%d\n",strlen(arr+0));
//    printf("%d\n",strlen(*arr));//strlen('a'),当做地址，把ascll当做地址进行访问，非法访问
//    printf("%d\n",strlen(arr[1]));
//    printf("%d\n",strlen(&arr));//这种写法结果和strlen(arr)是一样的，但是&arr应该放到指针数组里面，而strlen的定义是strlen（const char*)
//    printf("%d\n",strlen(&arr+1));//随机值
//    printf("%d\n",strlen(&arr[0]+1));//随机值
//
//}




//4.指针
//#include<stdio.h>
//int main(void)
//{
//    char *p="abcdef";//p接收的是字符串首个元素的地址
//    printf("%d\n",sizeof(p));//计算指针变量的大小
//    printf("%d\n",sizeof(p+1));//第二个元素的地址
//    printf("%d\n",sizeof(*p));
//    printf("%d\n",sizeof(p[0]));
//    printf("%d\n",sizeof(&p));
//    printf("%d\n",sizeof(&p+1));
//    printf("%d\n",sizeof(&p[0]+1));
//}





//5.二维数组
//#include<stdio.h>
//int main(void) {
//    int a[3][4] = {0};
//    printf("%d\n", sizeof(a));//48  3*4*4
//    printf("%d\n", sizeof(a[0][0]));//4    第一行第一列的那个元素
//    printf("%d\n", sizeof(a[0]));//16    a[0]相当于一维数组的数组名字，放在sizeof里面的效果是和一维数组相同

//    printf("%d\n", sizeof(a[0] + 1));//4/8  a[0]还是相当于一维数组的名字，也就代表着首元素的地址的名字，+1就是指向了第一行宿数组的第二个元素
//    printf("%d\n", sizeof(*(a[0] + 1)));//4/8   第一行第二个元素的大小
//    printf("%d\n", sizeof(a + 1));//4         除开两种情况，名字代表着首元素的的地址，二维数组的首元素是第一行的一位数组的地址，也就是&a[0],+1就是第二行的地址！！！，强调的是地址
//    printf("%d\n", sizeof(*(a + 1)));//16         等价于arr[1],也就是将整个第二行进行了解引用，得出来了第二行的首元素的地址
//    printf("%d\n", sizeof(&a[0] + 1));//8
//    printf("%d\n", sizeof(*(&a[0] + 1)));//16
//    printf("%d\n", sizeof(*a));//16          对二维数组名进行解引用得到的是一维数组的名字
//    printf("%d\n", sizeof(a[3]));//只是计算大小，并不参与运算，该算出来16还是16
//}
//二维数组的数组名除开两种情况代表的也是首元素的地址，只不过是第一行的整个地址，相当于  &（一维数组）

//再总结一次:sizeof(数组名），这里的数组名代表的是整个数组，计算的是整个数组的大小
//&数组名，这里的数组名表示整个数组，取出的是整个数组的地址
//初次之外所有的数组名都表示首元素的地址






//6.指针的笔试题（真题）

//int main(void)
//{
//    int a[5]={1,2,3,4,5};
//    int *ptr=(int *)(&a+1);
//    printf("%d%d",*(a+1),*(ptr-1));
//    return 0;
//}






//struct test
//{
//    int num;
//    char* pcname;
//    short sdate;
//    char cha[2];
//    short sba[4];
//}*p;//这里就是创建了一个结构体指针，p就是结构体指针变量
////假设 p的值为0x100000，如下表达式的值分别为多少
////已知，结构体test类型的变量大小是20个字节
//int main(void)
//{
//    p=(struct test*)0x100000;//p指针想要赋值，就得将值转换为符合p指针类型的数字
//    printf("%p\n",p+0x1);//0x1000014       0x1只是16进制的1，和10进制中的1没有什么区别，p+1,就相当于地址跳过去了一个结构体变量，0x0000001，这个1就是一个字节
      //指针变量+1，跳过的是那个类型的内存的大小  参考着int(*P)[5],理解，P+1就是跳过这个数组

//    printf("%p\n",(unsigned long)p+0x1);//0x1000001      先将p转换为整型数字1048576   整型数字+1就是+1，没有其他含义>>1048577，然后再转换为16进制进行打印

//    printf("%p\n",(unsigned int*)p+0x1);//0x100004      p被转换为一个无符号整型的指针，指针+1就是跳过指针类型的步长，再用16进制打印出来
//    return 0;
//
//    //指针+-整数取决于指针的类型
//}







//int main(void)
//{
//    int a[4]={1,2,3,4};
//    int *ptr1=(int*)(&a+1);//指针代表的是下个数组的首地址
//
//    int *ptr2=(int*)((int)a+1);//a的首元素地址转换为10进制加上1个字节，再被转换为指针的形式
//
//    printf("%x,%x",ptr1[-1],*ptr2);//0x4,0x
//    //ptr1[-1]=*(ptr1+(-1))=*(ptr-1)
//    return 0;
//    //大部分电脑都是小端存储模式，0000000001这种二进制的存储方式和16进制 00 00 00 00这种是一样的，只是将二进制转换为16进制进行显示，从左到右还是高地址-》低地址
//    //该数组在内存中的分配情况是 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00（以16进制的形式显示出来了）
//    //                         ^ptr2        往后查4个字节，也就是 00 00 00 02，小端的存储方式在读出来的时候应该是倒着的   ，所以屏幕上出现的是20 00 00 00
//}






//#include<stdio.h>
//int main(void)
//{
//    int a[3][2]={(0,1),(2,3),(4,5)};//逗号表达式，初始化为第一行1，3     第二行 5
//    int *p;
//    p=a[0];//p指向了第一行的地址
//    printf("%d\n",p[0]);//输出第一行中的第一个元素的地址
//    return 0;
//}






//#include<stdio.h>
//int main(void)
//{
//    int num=-4;
//    int a[5][5];//定义了一个五行五列的二维数组
//    int(*p)[4];//指针，指向的数组的大小为4个元素
//    p=a;//指向了二维数组的第一行，但是只包含了4个元素，有一个元素没有被包含进去,然后这个元素和下一行的三个元素再次构成此处定义的下一组的一维数组
//    printf("%p,%d\n",&p[4][2]-&a[4][2],&p[4][2]-&a[4][2]);//0xfffffffffffffffc    -4
//    //相同类型的指针变量相减，得到的是指针变量之间相差了多少个元素，用%p打印出来的是-4在内存中的补码
//    //-4补码：11111111111111111111111111111100        地址是一个无符号数，%p打印的时候就认为该数字的补码是地址
//    //一位数组的arr[3]=*(arr+3);
//    //此处的二维数组的p[4][2]=*(*(p+4)+2)
//    //指针变量相加减（必须是同一类型），值位指针和指针之间的元素个数（可以为负数）
//    //指针变量相减，得到的
//
//
//    //指针之间还可以比较大小，但是仅限同类型之间的指针进行相比较，类型不同比较没有意义，表示内存中他们指向的位置关系
//    //指针变量和整型数据相加，其值为指针指向的地址相加了多少个与指针类型相同的字节数
//
//    //a[5][5]的数组可以表示为
//    //[][][][][p+1]  [][][][p+2][]  [][][p+3][][]  [][p+4][][][]  [p+5][][][][]因为p是指向的大小为4个元素的数组
//    printf("%p\n",num);
//    return 0;
//}









//#include<stdio.h>
//int main(void)
//{
//    int aa[2][5]={1,2,3,4,5,6,7,8,9,10};
//    int *ptr1=(int*)(&aa+1);//指向了该数组末尾的地址
//    int *ptr2=(int*)(*(aa+1));//指向了第二行数组首元素的地址               对二维数组名字或者某一行进行解引用等于拿到这一行数组的名字
//    printf("%d,%d",*(ptr1-1),*(ptr2-1));//10,5
//    //在一维数组中 int arr[3];int *p=arr;*(p+2)==p[2]==*(arr+2)==arr[2]
//    return 0;
//
//}








//#include<stdio.h>
//int main(void)
//{
//    char *a[]={"work","at","alibaba"};//字符类型的指针数组，其指针都是指向了字符串的首元素，其元素的每个类型都是char*
//    char **pa=a;//pa存放了指针数组a的首元素的地址，并不是存放的&a的地址！！！首元素的地址也就是a[0]的地址，对这个地址解引用得出的内容是work的首元素的地址
//
//    pa++;
//    printf("%s\n",*pa);//*pa就是"at"的地址，对于%s来说，后面的数组名还是首元素的地址，直到碰见\0才会停止打印
//    return 0;
//}







//#include<stdio.h>
//int main(void)
//{
//    char *c[]={"enter","new","point","first"};//存放了4个地址，都是char*类型的
//
//    char **cp[]={c+3,c+2,c+1,c};//将上面的数组的各元素地址倒着存放了一遍，&c[3],&c[2],&c[1],&c[0]              首元素的地址，和c+3只是数值上一样，逻辑上是对cp解引用拿到的是c+3地址
//
//    char ***cpp=cp;//cpp存放着cp数组的首元素地址，cp的首元素地址解引用是&c[3]的地址
//
//    printf("%s\n",***++cpp);//point     此时cpp=cp+1      优先级++  -- 大于 * 大于 + -
//    printf("%s\n",*--*++cpp+3);//er   此时cpp=cp+2  c+1变更为c
//    printf("%s\n",*cpp[-2]+3);//st    **(cpp-2)+3
//    printf("%s\n",cpp[-1][-1]+1);//ew    **(*(cpp-1)-1)+1
//    return 0;
    //记住++ --之后值会变化的！！！，上面的++ --操作会影响下面的值的
    //a[4]=*(a+4)   一定要记住这个形式
//}

//#include<stdio.h>
//int main(void)
//{
//    char *c[]={"enter","new","point","first"};
//    char **cp[]={c+3,c+2,c+1,c};
//    char ***cpp=cp;//cpp指向了cp数组首元素的地址
//    printf("%s\n",**++cpp);//point
//    //首先cpp自增指向cp数组的第二个元素，解引用得到c+2,对c+2在进行解引用得到point的首元素地址，%s进行打印碰见\0停止，结果就是point
//    printf("%s\n",*--*++cpp+3);//er              这个操作也会使cp的第三个元素从c+1地址变为c地址          但是cpp依旧指向cp的第三个元素
//    //由于上面的自增cpp已经指向了cp数组的第二个元素，现在自增指向了cp数组的第三个元素，解引用得到c+1,c+1进行自减变更为c，对c数组的首元素进行解引用得到e的地址，在进行+3
//    //也就是将字符串的首元素+3用%s输出，碰见\0结束，也就是 er
//    printf("%s\n",*cpp[-2]+3);
//    //cpp现在已经指向了cp数组的第三个元素，cpp[-2]=*（cpp-2),所以这个算式为：*(*(cpp-2))+3.,cpp指向cp数组的第一个元素解引用得到的是c+3,对c+3进行解引用得到f的首元
//    //素的地址，f的地址+3以%s形式进行输出碰见\0停止，也就是 st
//    printf("%s\n",cpp[-1][-1]+1);//ew
//    //cpp现在依然指向cp数组的第三个元素，上面的形式为：*(*(cpp-1)-1)+1,首先对cpp-1解引用得到c+2,然后再对c+2-1进行解引用得到n元素的首地址，n元素的首地址+1，和上面
//    // 一样的计算过程  ew
//
//    return 0;
//
//}要给老子记得这个 arr[3]=*(arr+3)


//总结：首元素的地址并不是首元素的内容，首元素的地址进行解引用得到的才是首个内容,例如指针字符数组

//指针的大小（加 减）可以进行比较，但是比较的指针需要是同一类型。
